/*
 * Spring Security安全配置
 * 位于包：org.example.lethear.config
 * 
 * 这个类是Spring Security的核心配置类，负责：
 * 1. 配置HTTP安全规则
 * 2. 设置认证提供者
 * 3. 配置密码编码器
 * 4. 配置JWT过滤器
 * 5. 配置CORS跨域
 * 6. 配置异常处理
 * 
 * Spring Security是基于过滤器链的安全框架，通过一系列过滤器来实现认证和授权。
 * 这个配置类定义了整个应用的安全策略。
 */
package org.example.lethear.config;

import lombok.RequiredArgsConstructor;
import org.example.lethear.security.JwtAccessDeniedHandler;
import org.example.lethear.security.JwtAuthenticationEntryPoint;
import org.example.lethear.security.JwtAuthenticationFilter;
import org.example.lethear.security.UserDetailsServiceImpl;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.web.cors.CorsConfigurationSource;

/**
 * Spring Security安全配置类
 * 
 * @Configuration - 标识这是一个Spring配置类
 * @EnableWebSecurity - 启用Spring Security的Web安全功能
 * @EnableMethodSecurity(prePostEnabled = true) - 启用方法级别的安全控制，支持@PreAuthorize和@PostAuthorize注解
 * @RequiredArgsConstructor - Lombok注解，自动生成构造函数注入依赖
 */
@Configuration
@EnableWebSecurity
@EnableMethodSecurity(prePostEnabled = true)
@RequiredArgsConstructor
public class SecurityConfig {
    
    // 用户详情服务实现，用于加载用户信息
    private final UserDetailsServiceImpl userDetailsService;
    
    // JWT认证入口点，处理未认证的请求
    private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;
    
    // JWT访问拒绝处理器，处理权限不足的请求
    private final JwtAccessDeniedHandler jwtAccessDeniedHandler;
    
    // JWT认证过滤器，处理JWT令牌的验证
    private final JwtAuthenticationFilter jwtAuthenticationFilter;
    
    // CORS配置源，处理跨域请求
    private final CorsConfigurationSource corsConfigurationSource;
    
    /**
     * 配置密码编码器
     * 
     * BCryptPasswordEncoder是Spring Security推荐的密码编码器：
     * - 使用BCrypt哈希算法
     * - 自动生成盐值（salt）
     * - 每次编码结果都不同，但验证时能正确匹配
     * - 计算成本可配置，默认强度为10
     * 
     * @return PasswordEncoder 密码编码器实例
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        // 创建BCrypt密码编码器实例
        // 这个编码器会自动处理密码的加密和验证
        return new BCryptPasswordEncoder();
    }
    
    /**
     * 配置认证提供者
     * 
     * DaoAuthenticationProvider是Spring Security的默认认证提供者：
     * - 使用UserDetailsService加载用户信息
     * - 使用PasswordEncoder验证密码
     * - 支持用户名密码认证
     * 
     * @return AuthenticationProvider 认证提供者实例
     */
    @Bean
    public AuthenticationProvider authenticationProvider() {
        // 1. 创建DAO认证提供者，并传入密码编码器
        DaoAuthenticationProvider authProvider = new DaoAuthenticationProvider(passwordEncoder());
        
        // 2. 设置用户详情服务
        // 当用户登录时，认证提供者会调用此服务加载用户信息
        authProvider.setUserDetailsService(userDetailsService);
        
        // 3. 返回配置好的认证提供者
        return authProvider;
    }
    
    /**
     * 配置认证管理器
     * 
     * AuthenticationManager是Spring Security的核心接口：
     * - 负责处理认证请求
     * - 协调多个认证提供者
     * - 返回认证结果
     * 
     * @param config 认证配置对象
     * @return AuthenticationManager 认证管理器实例
     * @throws Exception 配置异常
     */
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception {
        // 从认证配置中获取认证管理器
        // Spring Security会自动配置好认证管理器，包括我们定义的认证提供者
        return config.getAuthenticationManager();
    }
    
    /**
     * 配置安全过滤器链
     * 
     * 这是Spring Security的核心配置方法，定义了整个应用的安全策略：
     * - 哪些URL需要认证
     * - 哪些URL需要特定权限
     * - 如何处理认证失败
     * - 如何处理权限不足
     * - 添加自定义过滤器
     * 
     * @param http HTTP安全配置对象
     * @return SecurityFilterChain 安全过滤器链
     * @throws Exception 配置异常
     */
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            // 1. 禁用CSRF保护
            // 由于我们使用JWT进行认证，不需要CSRF保护
            // CSRF主要用于防护基于Cookie的会话攻击
            .csrf(AbstractHttpConfigurer::disable)
            
            // 2. 配置CORS跨域
            // 允许前端应用从不同域名访问后端API
            .cors(cors -> cors.configurationSource(corsConfigurationSource))
            
            // 3. 配置会话管理策略
            // 设置为无状态（STATELESS），不创建HttpSession
            // 这符合JWT无状态认证的设计理念
            .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
            
            // 4. 配置异常处理
            .exceptionHandling(exception -> exception
                // 设置认证入口点，处理未认证的请求（401错误）
                .authenticationEntryPoint(jwtAuthenticationEntryPoint)
                // 设置访问拒绝处理器，处理权限不足的请求（403错误）
                .accessDeniedHandler(jwtAccessDeniedHandler)
            )
            
            // 5. 配置HTTP请求的授权规则
            // 这是最核心的安全配置，定义了哪些URL需要什么权限
            .authorizeHttpRequests(auth -> auth
                // 5.1 公开接口（无需认证）
                // 认证相关接口
                .requestMatchers("/auth/login", "/auth/register", "/auth/refresh").permitAll()
                
                // 用户名/邮箱可用性检查接口
                .requestMatchers("/auth/check-username", "/auth/check-email").permitAll()
                
                // 公共API接口
                .requestMatchers("/public/**").permitAll()
                
                // 健康检查接口
                .requestMatchers("/health", "/status").permitAll()
                
                // Spring Boot Actuator监控接口
                .requestMatchers("/actuator/**").permitAll()
                
                // Swagger API文档接口
                .requestMatchers("/swagger-ui/**", "/swagger-ui.html").permitAll()
                .requestMatchers("/v3/api-docs/**", "/swagger-resources/**").permitAll()
                
                // 错误处理接口
                .requestMatchers("/error").permitAll()
                
                // 5.2 需要特定角色的接口
                // 普通用户接口（需要USER角色或管理员角色）
                .requestMatchers("/user/**").hasAnyRole("USER", "ADMIN", "SYSTEM_ADMIN")
                .requestMatchers("/posts/**").hasAnyRole("USER", "ADMIN", "SYSTEM_ADMIN")
                .requestMatchers("/comments/**").hasAnyRole("USER", "ADMIN", "SYSTEM_ADMIN")
                .requestMatchers("/whisper/**").hasAnyRole("USER", "ADMIN", "SYSTEM_ADMIN")
                .requestMatchers("/whispers/**").hasAnyRole("USER", "ADMIN", "SYSTEM_ADMIN")
                .requestMatchers("/users/**").hasAnyRole("USER", "ADMIN", "SYSTEM_ADMIN")
                
                // 系统管理员接口（需要SYSTEM_ADMIN角色）
                .requestMatchers("/system/**").hasRole("SYSTEM_ADMIN")
                
                // 管理员接口（需要ADMIN或SYSTEM_ADMIN角色）
                .requestMatchers("/admin/**").hasAnyRole("ADMIN", "SYSTEM_ADMIN")
                .requestMatchers("/api/admin/**").hasAnyRole("ADMIN", "SYSTEM_ADMIN")
                
                // 5.3 默认规则
                // 其他所有请求都需要认证（但不要求特定角色）
                .anyRequest().authenticated()
            )
            
            // 6. 设置认证提供者
            // 告诉Spring Security使用我们配置的认证提供者
            .authenticationProvider(authenticationProvider())
            
            // 7. 添加JWT过滤器
            // 在用户名密码认证过滤器之前添加JWT过滤器
            // 这样每个请求都会先经过JWT验证
            .addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
        
        // 8. 构建并返回安全过滤器链
        return http.build();
    }
} 